package vn.smartoffice.auth;

import java.security.NoSuchAlgorithmException;

import javax.mail.AuthenticationFailedException;
import javax.mail.MessagingException;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Store;

import lombok.val;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import vn.smartoffice.entity.Role;
import vn.smartoffice.entity.User;
import vn.smartoffice.mapper.RoleMapper;
import vn.smartoffice.mapper.UserMapper;
import vn.smartoffice.promail.entity.MailAccount;
import vn.smartoffice.promail.entity.MailContactGroup;
import vn.smartoffice.promail.mapper.MailAccountMapper;
import vn.smartoffice.promail.mapper.MailContactGroupMapper;
import vn.smartoffice.promail.mapper.MailFolderMapper;

@Component
@Transactional
public class MailBaseAuthProvider implements AuthenticationProvider {

	private static final String DEFAULT_CONTACT_GROUP_NAME = "Danh bạ";

	@Autowired
	private UserMapper userMapper;

	@Autowired
	private MailAccountMapper mailAccountMapper;

	@Autowired
	private RoleMapper roleMapper;

	@Autowired
	private MailFolderMapper folderMapper;

	@Autowired
	private MailContactGroupMapper groupMapper;

	@Autowired
	private Session mailSession;

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		val email = authentication.getName();
		val password = authentication.getCredentials().toString();
		User user = userMapper.findByEmail(email);
		if (user == null) {
			try {
				Store store = mailSession.getStore("imaps");
				store.connect("imap.gmail.com", email, password);
				// create a new user
				user = new User();
				user.setEmail(email);
				user.setPassword(HashUtils.createPassword(password));
				user.setFirstName("Dung");
				user.setLastName("Nguyen Duc");
				userMapper.save(user);

				// add default role
				Role role = roleMapper.findByAuthority(Role.ROLE_USER);
				user.getRoles().add(role);
				userMapper.addRole(user.getId(), role.getId());

				// add primary email account.
				MailAccount mailAccount = new MailAccount();
				mailAccount.setEmail(email);
				mailAccount.setPassword(password);
				mailAccount.setImapServerUrl("imap.gmail.com");
				mailAccount.setImapServerPort(993);
				mailAccount.setStmpServerUrl("stmp.gmail.com");
				mailAccount.setStmpServerPort(465);
				mailAccount.setUser(user);
				mailAccountMapper.save(mailAccount);

				// add default group.
				MailContactGroup group = new MailContactGroup();
				group.setUser(user);
				group.setName(DEFAULT_CONTACT_GROUP_NAME);
				groupMapper.save(group);

				// close connection.
				store.close();
			} catch (AuthenticationFailedException ex) {
				throw new BadCredentialsException("Email or password is worng!");
			} catch (NoSuchProviderException ex) {
				throw new AuthenticationServiceException("");
			} catch (MessagingException e) {
				throw new AuthenticationServiceException("");
			} catch (NoSuchAlgorithmException e) {
				throw new InternalAuthenticationServiceException("");
			}
		}
		if (HashUtils.checkPassword(password, user.getPassword())) {
			return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getRoles());
		} else {
			throw new BadCredentialsException("Email or password is worng!");
		}
	}

	@Override
	public boolean supports(Class<?> authentication) {
		return UsernamePasswordAuthenticationToken.class.equals(authentication);
	}

}
